import React, { Suspense } from "react";
import type { Router as RemixRouter } from "@remix-run/router/dist/router";
import {createBrowserRouter, Navigate, RouteObject} from "react-router-dom";
import { RouteInfo } from ".";
import IndexPage from "./IndexPage";
import LogFactory from "log/LogFactory";
import LoadingComponent from "common/loading/LoadingComponent";
import ErrorComponent from "common/error/ErrorComponent";

const log = LogFactory.getLogger("RouteResolve");
/** 路由解析类,根据路由页面的文件夹中文件定义自动生成系统路由*/
export default class RouteResolve {
    /** 系统自动构建路由页面指定文件夹*/
    private readonly ROUTE_PAGE_DIR: string = "page";
    /** 路由配置信息的文件后缀 */
    private readonly ROUTE_CONFIG_SUFFIX: string = "/config.ts";
    /** 匹配全部路由中的一级路由的正则 */
        // 找到路径中只有一个/的路径,一级路径
        // ^: 匹配字符串的开始位置
        // \/: 匹配一个 / 字符
        // [^\/]+: 匹配除了 / 之外的任意字符，+ 表示至少匹配一个字符
        // $: 匹配字符串的结束位置
    private readonly REG_MAIN_PATH: RegExp = /^\/[^\/]+$/;
    private readonly REG_ROUTE_PATH: RegExp = new RegExp("/(index|[A-Z][A-Za-z]+).tsx");
    /** 系统全部路由页面文件信息 */
    private readonly context = require.context("page", true, /\.tsx$/, "lazy");
    /** 系统全部路由页面配置文件信息 */
    private readonly config = require.context('page', true, /\.(ts)$/, "sync");
    private readonly configKyes = this.config.keys();
    private readonly contextKeys = this.context.keys().filter(k => k.startsWith("page"));
    private readonly routeInfos: RouteInfo[];

    constructor() {
        this.routeInfos = this.contextKeys.map(k => {
            let routeConfig = null;
            // 获取路由配置信息
            let configPath = k.replace(this.REG_ROUTE_PATH, "/config.ts");
            if (this.configKyes.includes(configPath)) {
                routeConfig = this.config(configPath).default;
            }
            // 获取路由的路径
            let routePath = k.replace("page", "").replace(this.REG_ROUTE_PATH, "");
            let _config: RouteInfo = {
                key: k,
                path: routePath,
                mainRoute: routePath.match(this.REG_MAIN_PATH) !== null,
                config: routeConfig
            };
            return _config;
        });
    }
    /**
     * 解析路由
     */
    resolve(): RemixRouter {
        const rbs = this.getRouteObjectList();
        // log.debug("最终生成路由信息: ", rbs);
        return createBrowserRouter([
            {
                path: "/",
                element: <IndexPage />,
                // children: rbs,
                errorElement: <ErrorComponent/>
            },
            ...rbs,
            // {
            //     path: "*",
            //     element: <Navigate to={"/"}/>
            // }
        ]);
    }
    /**
     * 获取路由列表
     */
    private getRouteObjectList(): RouteObject[] {
        let mainRoute = this.routeInfos.filter(r => r.mainRoute).map(this.convertRoute.bind(this));
        let childrenRoute = this.routeInfos.filter(r => !r.mainRoute).map(this.convertRoute.bind(this));
        mainRoute.forEach(r => this.findRouteChildren(r, childrenRoute));
        return mainRoute;
    }

    /**
     *  找到路由的子路由
     */
    private findRouteChildren(route: RouteObject, routes: RouteObject[]) {
        // log.debug("查找路由的子路由: ", route.id)
        // 如果是索引路由则不需要查找子路由
        if (route.index) {
            // log.debug("路由为索引路由", route);
            return;
        }
        route.children = [];
        routes.forEach(r => {
            // 找到路由route的直接子路由
            if (r.id?.match(new RegExp("^" + route.id + "/[a-z-]+$")) !== null) {
                route.children?.push(r);
                // route.id = undefined;
                this.findRouteChildren(r, routes);
            }
        });
    }

    /**
     * 转换路由信息,将用户的配置文件配置到路由
     * 当路由文件的文件夹名称为index自动将其解析为索引路由
     * @param router 路由信息
     * @returns react-router路由对象
     */
    private convertRoute(router: RouteInfo): RouteObject {
        let { config, key, path } = router;
        if(path.endsWith("index")){
            config = {...config, index: true};
        }
        if (config) {
            return {
                id: path,
                index: config.index,
                path: config.index ? undefined : path,
                element: this.lazyLoadComponent(key)
            };
        }
        return {
            id: path,
            path: path,
            element: this.lazyLoadComponent(key)
        };
    }

    /**
     * 懒加载组件
     * @param key
     * @returns
     */
    private lazyLoadComponent = (key: string) => {
        let TempComponent = React.lazy(() => this.context(key));
        return (
            <Suspense fallback={<LoadingComponent/>}>
                <TempComponent />
            </Suspense>
        );
    };
}